/*
Copyright [2012] [Maria Eugenia Malagamba]
Copyright [2012] [Leandro Pellegrino]
Copyright [2012] [Julia Gambuzzi]
Copyright [2012] [Ignacio Capodanno]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "Server.h"

Server::Server(string puerto, string direccionIp){

	this->cantidadConexionesActivas = 0;
	this->cantidadMaximaConexiones = 2;
	this->puerto = puerto;
	this->direccionIp = direccionIp;
	this->socketServer = INVALID_SOCKET;
	this->cola1 = new vector<int>;
	this->cola2 = new vector<int>;
	this->socketsAceptados[0]=INVALID_SOCKET;
	this->socketsAceptados[1]=INVALID_SOCKET;
}

void Server::decrementarCantidadDeConexionesActivas(){
	this->cantidadConexionesActivas--;
}

void Server::cerrarConexionesSocketsAceptados(){
	for(int i=0; i < 2;i++){
		if(socketsAceptados[i] != INVALID_SOCKET){
			closesocket(socketsAceptados[i]);
			this->socketsAceptados[i]=INVALID_SOCKET;
			this->cantidadConexionesActivas --;
		}
	}
}

int Server::crearSocket(){

	if(WSAStartup(MAKEWORD(2,2), &this->wsa) != NO_ERROR)
	{
		cout << "ERROR: No se pudo iniciar WSAStartup \n";
		return -1;
	}

	struct addrinfo server_config;
	ZeroMemory(&server_config, sizeof(server_config));
	server_config.ai_family = AF_INET;
	server_config.ai_socktype = SOCK_STREAM;
	server_config.ai_protocol = IPPROTO_TCP;
	server_config.ai_flags = AI_PASSIVE;

	// Resuelve la direccion del servidor y el puerto.
	int resultado = getaddrinfo(NULL, this->puerto.c_str(), &server_config, &(this->adressInfo));
	if ( resultado != 0 ) {
		//TODO: Loguear lo que ocurre y terminar el proceso.
		cout << "ERROR: Fallo al obtener la informacion de la direccion:" << resultado << "\n";
		return -1;
	}

	this->socketServer = socket(adressInfo->ai_family, adressInfo->ai_socktype, adressInfo->ai_protocol);
	if(this->socketServer == INVALID_SOCKET)
	{
		//TODO: Loguear lo que ocurre.
		cout << "ERROR: No se pudo crear el Socket\n";
		return -1;
	}
	return 0;
}

int Server::escuchar(){

	int resultado = listen(this->socketServer, SOMAXCONN);
	if (resultado == SOCKET_ERROR) {
		//TODO:Loguear error.
		std::cout << "ERROR: No se puede escuchar sobre un Socket \n";
		return -1;
	}
	//Verifico que la conexion con el servidor se halla establecido, sino TIMEOUT
	fd_set pp;
	pp.fd_count = 1;
	pp.fd_array[0]= this->socketServer;

	timeval tv;
	tv.tv_sec = 120;
    tv.tv_usec = 0;

	int pip = select(0,&pp,NULL,NULL,&tv);
	if(pip <= 0){
		cout<<"No hay jugadores activos en el server, Timeout"<<endl;
		return -1;
	}	


	return 0;

	// Modo de desbloqueo en aceptar conexiones activado.
	//u_long modoBloqueo = 1;

	//resultado = ioctlsocket(this->socketServer, FIONBIO, &modoBloqueo );
	//if (resultado == SOCKET_ERROR)
	//	cout << "ioctlsocket fallo con error:" << resultado << endl;
}

int Server::enlazar(){

	//Establezco el socket de escucha TCP.
	int resultado = bind( this->socketServer, this->adressInfo->ai_addr, (int)this->adressInfo->ai_addrlen);
	if (resultado == SOCKET_ERROR) {
		//TODO:Loguear error.
		std::cout << "ERROR: No se puede escuchar sobre un Socket \n";
		return -1;
	}
	return 0;
}

int Server::aceptarConexion(){

	SOCKET aceptado = accept(this->socketServer,NULL,NULL);      

	if (aceptado != INVALID_SOCKET){
		socketsAceptados[cantidadConexionesActivas] = aceptado;
		this->cantidadConexionesActivas++;
		cout << "Cliente conectado" << endl;
		return (this->cantidadConexionesActivas -1);

	} else {
		cout << "Error al aceptar la conexion con un cliente " << WSAGetLastError()<< endl;
	}

	return -1;
}

int Server::enviar(char* mensaje, int longitud ,int numeroCliente){

	//envio a cliente
	//cout << "envio mensaje al cliente " << numeroCliente << endl;
	int resultado = send(socketsAceptados[numeroCliente],mensaje,longitud,0);
	if (resultado == SOCKET_ERROR) {
		cout << "Fallo el envio con error:" << WSAGetLastError() << endl;
		closesocket(socketsAceptados[numeroCliente]);
		this->socketsAceptados[numeroCliente] = NULL;
		this->decrementarCantidadDeConexionesActivas();
	}else{
		// cout << "Bytes enviados:" << resultado << endl;
	}
	return resultado;
}

int Server::recibir(char mensaje[],int longitud,int numeroCliente){

	int cantidadDatos=recv(socketsAceptados[numeroCliente],mensaje,longitud,0);

	if(cantidadDatos!=-1){
		//cout << "Mensaje recibido del Cliente: " << numeroCliente << " : " << endl;
		return cantidadDatos;
	} else if (cantidadDatos == 0) {
		//TODO: Se cierra la conexion.
		cout << "Error al recibir el mensaje" << endl;
	}

	return -1;
}

int Server::enviarEntero(int numero,int numeroCliente){

	return this->enviar((char*)&numero, sizeof(int),numeroCliente);

}

int Server::enviarString(string aEnviar,int numeroCliente){

	int tamanioString = aEnviar.size();
	if(this->enviarEntero(tamanioString,numeroCliente)){
		return this->enviar((char*)aEnviar.c_str(),tamanioString,numeroCliente);
	}else{
		return -1;
	}

}

int Server::recibirEntero(int numeroCliente){

	int numero = 0;
	this->recibir((char*)&numero, sizeof(int),numeroCliente);
	return numero;
}

string Server::recibirString(int numeroCliente){

	int tamBuffer = this->recibirEntero(numeroCliente);
	char* cadena = new char[tamBuffer+1];
	this->recibir(cadena,tamBuffer,numeroCliente);
	cadena[tamBuffer]= '\0';
	string toReturn(cadena);
	delete [] cadena;
	return toReturn;
}

int Server::enviarArchivo(string pathArchivo,int numeroCliente){

	int TAMANIOBUFFER =  1024;
	//variables
	char* buffer = NULL;
	char* ultBuffer = NULL;
	int tamBuffer = 0;
	int numMensajes = 1;
	int ultMensaje = 0;

	//Envio el nombre del archivo
	this->enviarString(pathArchivo,numeroCliente);

	//Abrimos el archivo
	fstream archivo;
	archivo.open(pathArchivo.c_str(),ios::binary | ios::in );

	if (archivo.good()){

		//Obtenie el tamanio del archivo
		archivo.seekg(0, ios::end);
		unsigned int tamanio = archivo.tellp();
		archivo.seekg(0, ios::beg);

		//si es mas grande que el buffer de transferencia lo dividimos
		if (tamanio > TAMANIOBUFFER){
			tamBuffer = TAMANIOBUFFER;
			numMensajes = tamanio / TAMANIOBUFFER;
			ultMensaje = tamanio % TAMANIOBUFFER; //resto de la division el ultimo buffer;

		}else {			
			//si no es mas grande que el buffer de transferencia es un solo msj
			tamBuffer= tamanio;
		}

		//Manda el tamanio del archivo a enviar, para que el cliente sepa
		//cuanto recibir
		this->enviarEntero(tamanio,numeroCliente);

		//Buffer para los mensajes
		buffer = new char[tamBuffer];


		int movimiento = 0;
		for( int i=0; i<numMensajes; i++){

			bool seMandoElMensaje = false;
			while (!seMandoElMensaje){

				archivo.seekg(movimiento, ios::beg);
				archivo.read(buffer,tamBuffer);

				//envio el buffer al cliente
				int resultado = this->enviar(buffer,tamBuffer,numeroCliente);

				int ok = this->recibirEntero(numeroCliente);

				if (resultado == ok){
					seMandoElMensaje = true;				
				}
								
			}
			//Agarro el siguiente paquete
			movimiento += tamBuffer;

		}
		delete [] buffer;

		//Para el ultimo mensaje si no dio exacta la division
		if (ultMensaje != 0){
			ultBuffer = new char[ultMensaje];

			bool seMandoElMensaje = false;
			while (!seMandoElMensaje){

				archivo.seekg(movimiento, ios::beg);
				archivo.read(ultBuffer,ultMensaje);

				//envio el buffer al cliente
				int resultado = this->enviar(ultBuffer,ultMensaje,numeroCliente);

				int ok = this->recibirEntero(numeroCliente);

				if (resultado == ok){
					seMandoElMensaje = true;				
				}
								
			}

			delete [] ultBuffer;

		}

		archivo.close();
		return 0;

	}else{
		cout<< "error no esta la imagen"<<endl;
		return -1;
	}

}

int Server::getCantidadDeConexionesActivas(){
	return this->cantidadConexionesActivas;

}

int Server::getCantidadMaximaConexiones(){
	return this->cantidadMaximaConexiones;

}


void Server::cerrarConexion(){
	cerrarConexionesSocketsAceptados();
	closesocket(this->socketServer);

	int pp = WSACleanup();
	cout <<"ERROR: "<<WSAGetLastError()<<endl;
}

Server::~Server(void){

}
